I. Preliminaries

Loading libraries

library("tidyverse")
library("tibble")
library("msigdbr")
library("ggplot2")
library("TCGAbiolinks")
library("RNAseqQC")
library("DESeq2")
library("ensembldb")
library("purrr")
library("magrittr")
library("vsn")
library("matrixStats")
library("dplyr")
library("grex")

II. Downloading TCGA gene expression data

Download gene expression data from The Cancer Genome Atlas (TCGA): - TCGA-COAD refers to the biospecimen data for colon adenocarcinoma. - STAR - Counts pertains to the raw counts.

query_tumor <- GDCquery(
  project = "TCGA-COAD",
  data.category = "Transcriptome Profiling",
  data.type = "Gene Expression Quantification",
  experimental.strategy = "RNA-Seq",
  workflow.type = "STAR - Counts",
  access = "open",
  sample.type = "Primary Tumor"
)
tumor <- getResults(query_tumor)
tumor
query_normal <- GDCquery(
  project = "TCGA-COAD",
  data.category = "Transcriptome Profiling",
  data.type = "Gene Expression Quantification",
  experimental.strategy = "RNA-Seq",
  workflow.type = "STAR - Counts",
  access = "open",
  sample.type = "Solid Tissue Normal"
)
normal <- getResults(query_normal)
normal

Consider only samples with both normal and malignant tissues.

submitter_ids <- inner_join(tumor, normal, by = "cases.submitter_id") %>%
  dplyr::select(cases.submitter_id)
tumor <- tumor %>%
  dplyr::filter(cases.submitter_id %in% submitter_ids$cases.submitter_id)
normal <- normal %>%
  dplyr::filter(cases.submitter_id %in% submitter_ids$cases.submitter_id)

samples <- rbind(tumor, normal)
invisible(unique(samples$sample_type))
samples

Download only samples with both normal and malignant tissues.

To impose this filtering, we set the barcode argument of GDCquery to samples$sample.submitter_id (which was generated in the previous cell).

query_coad <- GDCquery(
  project = "TCGA-COAD",
  data.category = "Transcriptome Profiling",
  data.type = "Gene Expression Quantification",
  experimental.strategy = "RNA-Seq",
  workflow.type = "STAR - Counts",
  access = "open",
  sample.type = c("Solid Tissue Normal", "Primary Tumor"),
  barcode = as.list(samples$sample.submitter_id)
)

If this is your first time running this notebook (i.e., you have not yet downloaded the results of the query in the previous block), uncomment the code block below.

# GDCdownload(query_coad)

Running the code block above should generate and populate a directory named GDCdata.

III. Data preprocessing

Construct the RNA-seq count matrix.

tcga_coad_data <- GDCprepare(query_coad, summarizedExperiment = TRUE)
count_matrix <- assay(tcga_coad_data, "unstranded")

# Remove duplicate entries
count_matrix_df <- data.frame(count_matrix)
count_matrix_df <- count_matrix_df[!duplicated(count_matrix_df), ]
count_matrix <- data.matrix(count_matrix_df)
rownames(count_matrix) <- cleanid(rownames(count_matrix))
count_matrix <- count_matrix[!(duplicated(rownames(count_matrix)) | duplicated(rownames(count_matrix), fromLast = TRUE)), ]

head(count_matrix[1:5, 1:4])
                TCGA.AA.3522.01A.01R.0821.07 TCGA.A6.2678.01A.01R.0821.07 TCGA.A6.5665.01B.03R.2302.07 TCGA.AZ.6600.01A.11R.1774.07
ENSG00000000003                         4069                         4088                          670                         3195
ENSG00000000005                           22                           38                           10                           14
ENSG00000000419                         1070                         1577                          535                         2460
ENSG00000000457                          386                          625                          425                          624
ENSG00000000460                          166                          441                          253                          467

Format the samples table so that it can be fed as input to DESeq2.

rownames(samples) <- samples$cases
samples <- samples %>%
  dplyr::select(case = "cases.submitter_id", type = "sample_type")
samples$type <- str_replace(samples$type, "Solid Tissue Normal", "normal")
samples$type <- str_replace(samples$type, "Primary Tumor", "tumor")

DESeq2 requires the row names of samples should be identical to the column names of count_matrix.

colnames(count_matrix) <- gsub(x = colnames(count_matrix), pattern = "\\.", replacement = "-")
count_matrix <- count_matrix[, rownames(samples)]

# Sanity check
all(colnames(count_matrix) == rownames(samples))

IV. Differential gene expression analysis

References:

Construct the DESeqDataSet object.

dds <- DESeqDataSetFromMatrix(
  countData = count_matrix,
  colData = samples,
  design = ~type
)
Warning: some variables in design formula are characters, converting to factors

Quality Control

Display quality control (QC) plots (refer to https://cran.r-project.org/web/packages/RNAseqQC/vignettes/introduction.html)

  • Total sample counts
    • Total number of counts for each sample
    • We typically expect all samples to have total counts within the same order of magnitude
  • Library complexity
    • What fraction of counts is taken up by what fraction of genes
    • Samples showing a different library complexity than the rest might be considered low quality
  • Gene detection
    • Number of detected genes for each sample
plot_total_counts(dds)

plot_library_complexity(dds)

plot_gene_detection(dds)

Perform gene filtering.

We determined min_count empirically by looking at the red trend line in the variance stabilization plot. Ideally, this trend line should be flat (i.e., stable).

  • Not setting any min_count resulted in a steep, upward-sloping trend line.
  • Setting it to 5 also resulted in a steep, upward-sloping trend line.
dds <- filter_genes(dds, min_count = 10)

Transform the read counts.

From https://chipster.csc.fi/manual/deseq2-transform.html:
You can use the resulting transformed values only for visualization and clustering, not for differential expression analysis which needs raw counts.

vsd <- vst(dds)
mean_sd_plot(vsd)

Check the clustering of the samples.

If you encounter the error Error in loadNamespace(x) : there is no package called 'ComplexHeatmap', uncomment and run the following code block:

# install.packages("devtools", dependencies = TRUE)
# devtools::install_github("jokergoo/ComplexHeatmap")
set.seed(42)
plot_sample_clustering(vsd, anno_vars = c("type"), distance = "euclidean")

Perform principal component analysis (PCA).

plot_pca(vsd, PC_x = 1, PC_y = 2, shape_by = "type")

Regulated Cell Death

Refer to 1. Exploratory Data Analysis - MSigDB Gene Sets + GTEx TPM.Rmd for more detailed documentation on obtaining the gene sets.

Necroptosis

Fetch the necroptosis gene set.

necroptosis.genes <- msigdbr(species = "human", category = "C5", subcategory = "GO:BP") %>%
  dplyr::filter(gs_name == "GOBP_NECROPTOTIC_SIGNALING_PATHWAY")
necroptosis.genes

Filter the genes to include only those in the necroptosis gene set.

rownames(necroptosis.genes) <- necroptosis.genes$ensembl_gene
coad_necroptosis <- count_matrix[rownames(count_matrix) %in% necroptosis.genes$ensembl_gene, ]
coad_necroptosis <- coad_necroptosis[, rownames(samples)]

# Check if all samples in the counts dataframe are in the samples dataframe
all(colnames(coad_necroptosis) == rownames(samples))

Perform differential gene expression analysis.

dds <- DESeqDataSetFromMatrix(
  countData = coad_necroptosis,
  colData = samples,
  design = ~type
)
Warning: some variables in design formula are characters, converting to factors
dds <- filter_genes(dds, min_count = 10)
dds$type <- relevel(dds$type, ref = "normal")
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
res <- results(dds)
summary(res)

out of 8 with nonzero total read count
adjusted p-value < 0.1
LFC > 0 (up)       : 2, 25%
LFC < 0 (down)     : 2, 25%
outliers [1]       : 0, 0%
low counts [2]     : 0, 0%
(mean count < 38)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results

Prettify the display of results.

deseq.results <- res
deseq.bbl.data <- data.frame(
  row.names = rownames(deseq.results),
  baseMean = deseq.results$baseMean,
  log2FoldChange = deseq.results$log2FoldChange,
  lfcSE = deseq.results$lfcSE,
  stat = deseq.results$stat,
  pvalue = deseq.results$pvalue,
  padj = deseq.results$padj,
  cancer_type = "Colon",
  gene_symbol = necroptosis.genes[rownames(deseq.results), "gene_symbol"]
)
deseq.bbl.data

Plot the results.

ggplot(deseq.bbl.data, aes(x = cancer_type, y = gene_symbol, size = padj, fill = log2FoldChange)) +
  geom_point(alpha = 0.5, shape = 21, color = "black") +
  scale_size(trans = "reverse") +
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", limits = c(min(deseq.bbl.data$log2FoldChange), max(deseq.bbl.data$log2FoldChange))) +
  theme_minimal() +
  theme(legend.position = "bottom") +
  theme(legend.position = "bottom") +
  labs(size = "FDR", fill = "log2 FC", x = "Cancer type", y = "Gene")

Ferroptosis

Fetch the ferroptosis gene set.

ferroptosis.genes <- msigdbr(species = "human", category = "C2", subcategory = "CP:WIKIPATHWAYS") %>%
  dplyr::filter(gs_name == "WP_FERROPTOSIS")
ferroptosis.genes

Filter the genes to include only those in the ferroptosis gene set.

rownames(ferroptosis.genes) <- ferroptosis.genes$ensembl_gene
coad_ferroptosis <- count_matrix[rownames(count_matrix) %in% ferroptosis.genes$ensembl_gene, ]
coad_ferroptosis <- coad_ferroptosis[, rownames(samples)]

# Check if all samples in the counts dataframe are in the samples dataframe
all(colnames(coad_ferroptosis) == rownames(samples))

Perform differential gene expression analysis.

dds <- DESeqDataSetFromMatrix(
  countData = coad_ferroptosis,
  colData = samples,
  design = ~type
)
Warning: some variables in design formula are characters, converting to factors
dds <- filter_genes(dds, min_count = 10)
dds$type <- relevel(dds$type, ref = "normal")
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 2 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
res <- results(dds)
summary(res)

out of 63 with nonzero total read count
adjusted p-value < 0.1
LFC > 0 (up)       : 24, 38%
LFC < 0 (down)     : 19, 30%
outliers [1]       : 0, 0%
low counts [2]     : 0, 0%
(mean count < 8)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results

Prettify the display of results.

deseq.results <- res
deseq.bbl.data <- data.frame(
  row.names = rownames(deseq.results),
  baseMean = deseq.results$baseMean,
  log2FoldChange = deseq.results$log2FoldChange,
  lfcSE = deseq.results$lfcSE,
  stat = deseq.results$stat,
  pvalue = deseq.results$pvalue,
  padj = deseq.results$padj,
  cancer_type = "Colon",
  gene_symbol = ferroptosis.genes[rownames(deseq.results), "gene_symbol"]
)
deseq.bbl.data

Plot the results.

ggplot(deseq.bbl.data, aes(x = cancer_type, y = gene_symbol, size = padj, fill = log2FoldChange)) +
  geom_point(alpha = 0.5, shape = 21, color = "black") +
  scale_size(trans = "reverse") +
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", limits = c(min(deseq.bbl.data$log2FoldChange), max(deseq.bbl.data$log2FoldChange))) +
  theme_minimal() +
  theme(legend.position = "bottom") +
  theme(legend.position = "bottom") +
  labs(size = "FDR", fill = "log2 FC", x = "Cancer type", y = "Gene")

Pyroptosis

Fetch the pyroptosis gene set.

pyroptosis.genes <- msigdbr(species = "human", category = "C2", subcategory = "CP:REACTOME") %>%
  dplyr::filter(gs_name == "REACTOME_PYROPTOSIS")
pyroptosis.genes

Filter the genes to include only those in the pyroptosis gene set.

rownames(pyroptosis.genes) <- pyroptosis.genes$ensembl_gene
coad_pyroptosis <- count_matrix[rownames(count_matrix) %in% pyroptosis.genes$ensembl_gene, ]
coad_pyroptosis <- coad_pyroptosis[, rownames(samples)]

# Check if all samples in the counts dataframe are in the samples dataframe
all(colnames(coad_pyroptosis) == rownames(samples))

Perform differential gene expression analysis.

dds <- DESeqDataSetFromMatrix(
  countData = coad_pyroptosis,
  colData = samples,
  design = ~type
)
Warning: some variables in design formula are characters, converting to factors
dds <- filter_genes(dds, min_count = 10)
dds$type <- relevel(dds$type, ref = "normal")
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
-- replacing outliers and refitting for 1 genes
-- DESeq argument 'minReplicatesForReplace' = 7 
-- original counts are preserved in counts(dds)
estimating dispersions
fitting model and testing
res <- results(dds)
summary(res)

out of 27 with nonzero total read count
adjusted p-value < 0.1
LFC > 0 (up)       : 10, 37%
LFC < 0 (down)     : 7, 26%
outliers [1]       : 0, 0%
low counts [2]     : 0, 0%
(mean count < 16)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results

Prettify the display of results.

deseq.results <- res
deseq.bbl.data <- data.frame(
  row.names = rownames(deseq.results),
  baseMean = deseq.results$baseMean,
  log2FoldChange = deseq.results$log2FoldChange,
  lfcSE = deseq.results$lfcSE,
  stat = deseq.results$stat,
  pvalue = deseq.results$pvalue,
  padj = deseq.results$padj,
  cancer_type = "Colon",
  gene_symbol = pyroptosis.genes[rownames(deseq.results), "gene_symbol"]
)
deseq.bbl.data

Plot the results.

ggplot(deseq.bbl.data, aes(x = cancer_type, y = gene_symbol, size = padj, fill = log2FoldChange)) +
  geom_point(alpha = 0.5, shape = 21, color = "black") +
  scale_size(trans = "reverse") +
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", limits = c(min(deseq.bbl.data$log2FoldChange), max(deseq.bbl.data$log2FoldChange))) +
  theme_minimal() +
  theme(legend.position = "bottom") +
  theme(legend.position = "bottom") +
  labs(size = "FDR", fill = "log2 FC", x = "Cancer type", y = "Gene")


  1. De La Salle University, Manila, Philippines, ↩︎

  2. De La Salle University, Manila, Philippines, ↩︎

  3. De La Salle University, Manila, Philippines, ↩︎

LS0tDQp0aXRsZTogIkRpZmZlcmVudGlhbCBHZW5lIEV4cHJlc3Npb24gQW5hbHlzaXMiDQpzdWJ0aXRsZTogIkNvbG9yZWN0YWwgQ2FuY2VyIHwgTmVjcm9wdG9zaXMsIEZlcnJvcHRvc2lzICYgUHlyb3B0b3NpcyINCmF1dGhvcjogDQogIC0gS2ltIFdpbGxpYW1lIExlZV5bRGUgTGEgU2FsbGUgVW5pdmVyc2l0eSwgTWFuaWxhLCBQaGlsaXBwaW5lcywga2ltX2xlZWpyYUBkbHN1LmVkdS5waF0NCiAgLSBNYXJrIEVkd2FyZCBNLiBHb256YWxlc15bRGUgTGEgU2FsbGUgVW5pdmVyc2l0eSwgTWFuaWxhLCBQaGlsaXBwaW5lcywgZ29uemFsZXMubWFya2Vkd2FyZEBnbWFpbC5jb21dDQogIC0gRHIuIEFuaXNoIE0uUy4gU2hyZXN0aGFeW0RlIExhIFNhbGxlIFVuaXZlcnNpdHksIE1hbmlsYSwgUGhpbGlwcGluZXMsIGFuaXNoLnNocmVzdGhhQGRsc3UuZWR1LnBoXQ0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgSS4gUHJlbGltaW5hcmllcw0KDQojIyMgTG9hZGluZyBsaWJyYXJpZXMNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSgidGliYmxlIikNCmxpYnJhcnkoIm1zaWdkYnIiKQ0KbGlicmFyeSgiZ2dwbG90MiIpDQpsaWJyYXJ5KCJUQ0dBYmlvbGlua3MiKQ0KbGlicmFyeSgiUk5Bc2VxUUMiKQ0KbGlicmFyeSgiREVTZXEyIikNCmxpYnJhcnkoImVuc2VtYmxkYiIpDQpsaWJyYXJ5KCJwdXJyciIpDQpsaWJyYXJ5KCJtYWdyaXR0ciIpDQpsaWJyYXJ5KCJ2c24iKQ0KbGlicmFyeSgibWF0cml4U3RhdHMiKQ0KbGlicmFyeSgiZHBseXIiKQ0KbGlicmFyeSgiZ3JleCIpDQpgYGANCg0KIyMgSUkuIERvd25sb2FkaW5nIFRDR0EgZ2VuZSBleHByZXNzaW9uIGRhdGEgDQoNCkRvd25sb2FkIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGZyb20gVGhlIENhbmNlciBHZW5vbWUgQXRsYXMgKFRDR0EpOg0KLSBgVENHQS1DT0FEYCByZWZlcnMgdG8gdGhlIGJpb3NwZWNpbWVuIGRhdGEgZm9yIGNvbG9uIGFkZW5vY2FyY2lub21hLg0KLSBgU1RBUiAtIENvdW50c2AgcGVydGFpbnMgdG8gdGhlIHJhdyBjb3VudHMuDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KcXVlcnlfdHVtb3IgPC0gR0RDcXVlcnkoDQogIHByb2plY3QgPSAiVENHQS1DT0FEIiwNCiAgZGF0YS5jYXRlZ29yeSA9ICJUcmFuc2NyaXB0b21lIFByb2ZpbGluZyIsDQogIGRhdGEudHlwZSA9ICJHZW5lIEV4cHJlc3Npb24gUXVhbnRpZmljYXRpb24iLA0KICBleHBlcmltZW50YWwuc3RyYXRlZ3kgPSAiUk5BLVNlcSIsDQogIHdvcmtmbG93LnR5cGUgPSAiU1RBUiAtIENvdW50cyIsDQogIGFjY2VzcyA9ICJvcGVuIiwNCiAgc2FtcGxlLnR5cGUgPSAiUHJpbWFyeSBUdW1vciINCikNCnR1bW9yIDwtIGdldFJlc3VsdHMocXVlcnlfdHVtb3IpDQp0dW1vcg0KYGBgDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQ0KcXVlcnlfbm9ybWFsIDwtIEdEQ3F1ZXJ5KA0KICBwcm9qZWN0ID0gIlRDR0EtQ09BRCIsDQogIGRhdGEuY2F0ZWdvcnkgPSAiVHJhbnNjcmlwdG9tZSBQcm9maWxpbmciLA0KICBkYXRhLnR5cGUgPSAiR2VuZSBFeHByZXNzaW9uIFF1YW50aWZpY2F0aW9uIiwNCiAgZXhwZXJpbWVudGFsLnN0cmF0ZWd5ID0gIlJOQS1TZXEiLA0KICB3b3JrZmxvdy50eXBlID0gIlNUQVIgLSBDb3VudHMiLA0KICBhY2Nlc3MgPSAib3BlbiIsDQogIHNhbXBsZS50eXBlID0gIlNvbGlkIFRpc3N1ZSBOb3JtYWwiDQopDQpub3JtYWwgPC0gZ2V0UmVzdWx0cyhxdWVyeV9ub3JtYWwpDQpub3JtYWwNCmBgYA0KQ29uc2lkZXIgb25seSBzYW1wbGVzIHdpdGggYm90aCBub3JtYWwgYW5kIG1hbGlnbmFudCB0aXNzdWVzLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCnN1Ym1pdHRlcl9pZHMgPC0gaW5uZXJfam9pbih0dW1vciwgbm9ybWFsLCBieSA9ICJjYXNlcy5zdWJtaXR0ZXJfaWQiKSAlPiUNCiAgZHBseXI6OnNlbGVjdChjYXNlcy5zdWJtaXR0ZXJfaWQpDQp0dW1vciA8LSB0dW1vciAlPiUNCiAgZHBseXI6OmZpbHRlcihjYXNlcy5zdWJtaXR0ZXJfaWQgJWluJSBzdWJtaXR0ZXJfaWRzJGNhc2VzLnN1Ym1pdHRlcl9pZCkNCm5vcm1hbCA8LSBub3JtYWwgJT4lDQogIGRwbHlyOjpmaWx0ZXIoY2FzZXMuc3VibWl0dGVyX2lkICVpbiUgc3VibWl0dGVyX2lkcyRjYXNlcy5zdWJtaXR0ZXJfaWQpDQoNCnNhbXBsZXMgPC0gcmJpbmQodHVtb3IsIG5vcm1hbCkNCmludmlzaWJsZSh1bmlxdWUoc2FtcGxlcyRzYW1wbGVfdHlwZSkpDQpzYW1wbGVzDQpgYGANCg0KRG93bmxvYWQgb25seSBzYW1wbGVzIHdpdGggYm90aCBub3JtYWwgYW5kIG1hbGlnbmFudCB0aXNzdWVzLg0KDQpUbyBpbXBvc2UgdGhpcyBmaWx0ZXJpbmcsIHdlIHNldCB0aGUgYGJhcmNvZGVgIGFyZ3VtZW50IG9mIGBHRENxdWVyeWAgdG8gYHNhbXBsZXMkc2FtcGxlLnN1Ym1pdHRlcl9pZGAgKHdoaWNoIHdhcyBnZW5lcmF0ZWQgaW4gdGhlIHByZXZpb3VzIGNlbGwpLg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCnF1ZXJ5X2NvYWQgPC0gR0RDcXVlcnkoDQogIHByb2plY3QgPSAiVENHQS1DT0FEIiwNCiAgZGF0YS5jYXRlZ29yeSA9ICJUcmFuc2NyaXB0b21lIFByb2ZpbGluZyIsDQogIGRhdGEudHlwZSA9ICJHZW5lIEV4cHJlc3Npb24gUXVhbnRpZmljYXRpb24iLA0KICBleHBlcmltZW50YWwuc3RyYXRlZ3kgPSAiUk5BLVNlcSIsDQogIHdvcmtmbG93LnR5cGUgPSAiU1RBUiAtIENvdW50cyIsDQogIGFjY2VzcyA9ICJvcGVuIiwNCiAgc2FtcGxlLnR5cGUgPSBjKCJTb2xpZCBUaXNzdWUgTm9ybWFsIiwgIlByaW1hcnkgVHVtb3IiKSwNCiAgYmFyY29kZSA9IGFzLmxpc3Qoc2FtcGxlcyRzYW1wbGUuc3VibWl0dGVyX2lkKQ0KKQ0KYGBgDQpJZiB0aGlzIGlzIHlvdXIgZmlyc3QgdGltZSBydW5uaW5nIHRoaXMgbm90ZWJvb2sgKGkuZS4sIHlvdSBoYXZlIG5vdCB5ZXQgZG93bmxvYWRlZCB0aGUgcmVzdWx0cyBvZiB0aGUgcXVlcnkgaW4gdGhlIHByZXZpb3VzIGJsb2NrKSwgdW5jb21tZW50IHRoZSBjb2RlIGJsb2NrIGJlbG93Lg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCiMgR0RDZG93bmxvYWQocXVlcnlfY29hZCkNCmBgYA0KDQpSdW5uaW5nIHRoZSBjb2RlIGJsb2NrIGFib3ZlIHNob3VsZCBnZW5lcmF0ZSBhbmQgcG9wdWxhdGUgYSBkaXJlY3RvcnkgbmFtZWQgYEdEQ2RhdGFgLg0KDQojIyBJSUkuIERhdGEgcHJlcHJvY2Vzc2luZw0KDQpDb25zdHJ1Y3QgdGhlIFJOQS1zZXEgY291bnQgbWF0cml4Lg0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobyA9IFRSVUUsIHJlc3VsdHM9ImhpZGUifQ0KdGNnYV9jb2FkX2RhdGEgPC0gR0RDcHJlcGFyZShxdWVyeV9jb2FkLCBzdW1tYXJpemVkRXhwZXJpbWVudCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpjb3VudF9tYXRyaXggPC0gYXNzYXkodGNnYV9jb2FkX2RhdGEsICJ1bnN0cmFuZGVkIikNCg0KIyBSZW1vdmUgZHVwbGljYXRlIGVudHJpZXMNCmNvdW50X21hdHJpeF9kZiA8LSBkYXRhLmZyYW1lKGNvdW50X21hdHJpeCkNCmNvdW50X21hdHJpeF9kZiA8LSBjb3VudF9tYXRyaXhfZGZbIWR1cGxpY2F0ZWQoY291bnRfbWF0cml4X2RmKSwgXQ0KY291bnRfbWF0cml4IDwtIGRhdGEubWF0cml4KGNvdW50X21hdHJpeF9kZikNCnJvd25hbWVzKGNvdW50X21hdHJpeCkgPC0gY2xlYW5pZChyb3duYW1lcyhjb3VudF9tYXRyaXgpKQ0KY291bnRfbWF0cml4IDwtIGNvdW50X21hdHJpeFshKGR1cGxpY2F0ZWQocm93bmFtZXMoY291bnRfbWF0cml4KSkgfCBkdXBsaWNhdGVkKHJvd25hbWVzKGNvdW50X21hdHJpeCksIGZyb21MYXN0ID0gVFJVRSkpLCBdDQoNCmhlYWQoY291bnRfbWF0cml4WzE6NSwgMTo0XSkNCmBgYA0KRm9ybWF0IHRoZSBgc2FtcGxlc2AgdGFibGUgc28gdGhhdCBpdCBjYW4gYmUgZmVkIGFzIGlucHV0IHRvIERFU2VxMi4NCg0KYGBge3J9DQpyb3duYW1lcyhzYW1wbGVzKSA8LSBzYW1wbGVzJGNhc2VzDQpzYW1wbGVzIDwtIHNhbXBsZXMgJT4lDQogIGRwbHlyOjpzZWxlY3QoY2FzZSA9ICJjYXNlcy5zdWJtaXR0ZXJfaWQiLCB0eXBlID0gInNhbXBsZV90eXBlIikNCnNhbXBsZXMkdHlwZSA8LSBzdHJfcmVwbGFjZShzYW1wbGVzJHR5cGUsICJTb2xpZCBUaXNzdWUgTm9ybWFsIiwgIm5vcm1hbCIpDQpzYW1wbGVzJHR5cGUgPC0gc3RyX3JlcGxhY2Uoc2FtcGxlcyR0eXBlLCAiUHJpbWFyeSBUdW1vciIsICJ0dW1vciIpDQpgYGANCg0KREVTZXEyIHJlcXVpcmVzIHRoZSByb3cgbmFtZXMgb2YgYHNhbXBsZXNgIHNob3VsZCBiZSBpZGVudGljYWwgdG8gdGhlIGNvbHVtbiBuYW1lcyBvZiBgY291bnRfbWF0cml4YC4NCg0KYGBge3IsIGVjaG8gPSBUUlVFLCByZXN1bHRzPSJoaWRlIn0NCmNvbG5hbWVzKGNvdW50X21hdHJpeCkgPC0gZ3N1Yih4ID0gY29sbmFtZXMoY291bnRfbWF0cml4KSwgcGF0dGVybiA9ICJcXC4iLCByZXBsYWNlbWVudCA9ICItIikNCmNvdW50X21hdHJpeCA8LSBjb3VudF9tYXRyaXhbLCByb3duYW1lcyhzYW1wbGVzKV0NCg0KIyBTYW5pdHkgY2hlY2sNCmFsbChjb2xuYW1lcyhjb3VudF9tYXRyaXgpID09IHJvd25hbWVzKHNhbXBsZXMpKQ0KYGBgDQoNCiMjIElWLiBEaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFuYWx5c2lzDQoNClJlZmVyZW5jZXM6IA0KDQotIE9mZmljaWFsIGRvY3VtZW50YXRpb246IGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9ERVNlcTIvaW5zdC9kb2MvREVTZXEyLmh0bWwNCi0gR29vZCBiYWxhbmNlIG9mIHRoZW9yeSBhbmQgaGFuZHMtb246IGh0dHBzOi8vaGJjdHJhaW5pbmcuZ2l0aHViLmlvL0RHRV93b3Jrc2hvcC9sZXNzb25zLzA0X0RHRV9ERVNlcTJfYW5hbHlzaXMuaHRtbA0KLSBRdWFsaXR5IGNvbnRyb2w6IGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9STkFzZXFRQy92aWduZXR0ZXMvaW50cm9kdWN0aW9uLmh0bWwNCg0KQ29uc3RydWN0IHRoZSBgREVTZXFEYXRhU2V0YCBvYmplY3QuDQoNCmBgYHtyfQ0KZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoDQogIGNvdW50RGF0YSA9IGNvdW50X21hdHJpeCwNCiAgY29sRGF0YSA9IHNhbXBsZXMsDQogIGRlc2lnbiA9IH50eXBlDQopDQpgYGANCg0KIyMjIFF1YWxpdHkgQ29udHJvbA0KDQpEaXNwbGF5IHF1YWxpdHkgY29udHJvbCAoUUMpIHBsb3RzIChyZWZlciB0byBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUk5Bc2VxUUMvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKQ0KDQotIFRvdGFsIHNhbXBsZSBjb3VudHMgIA0KICAtIFRvdGFsIG51bWJlciBvZiBjb3VudHMgZm9yIGVhY2ggc2FtcGxlDQogIC0gV2UgdHlwaWNhbGx5IGV4cGVjdCBhbGwgc2FtcGxlcyB0byBoYXZlIHRvdGFsIGNvdW50cyB3aXRoaW4gdGhlIHNhbWUgb3JkZXIgb2YgbWFnbml0dWRlDQogIA0KLSBMaWJyYXJ5IGNvbXBsZXhpdHkNCiAgLSBXaGF0IGZyYWN0aW9uIG9mIGNvdW50cyBpcyB0YWtlbiB1cCBieSB3aGF0IGZyYWN0aW9uIG9mIGdlbmVzDQogIC0gU2FtcGxlcyBzaG93aW5nIGEgZGlmZmVyZW50IGxpYnJhcnkgY29tcGxleGl0eSB0aGFuIHRoZSByZXN0IG1pZ2h0IGJlIGNvbnNpZGVyZWQgbG93IHF1YWxpdHkNCiAgDQotIEdlbmUgZGV0ZWN0aW9uDQogIC0gTnVtYmVyIG9mIGRldGVjdGVkIGdlbmVzIGZvciBlYWNoIHNhbXBsZQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCnBsb3RfdG90YWxfY291bnRzKGRkcykNCnBsb3RfbGlicmFyeV9jb21wbGV4aXR5KGRkcykNCnBsb3RfZ2VuZV9kZXRlY3Rpb24oZGRzKQ0KYGBgDQpQZXJmb3JtIGdlbmUgZmlsdGVyaW5nLg0KDQpXZSBkZXRlcm1pbmVkIGBtaW5fY291bnRgIGVtcGlyaWNhbGx5IGJ5IGxvb2tpbmcgYXQgdGhlIHJlZCB0cmVuZCBsaW5lIGluIHRoZSB2YXJpYW5jZSBzdGFiaWxpemF0aW9uIHBsb3QuIElkZWFsbHksIHRoaXMgdHJlbmQgbGluZSBzaG91bGQgYmUgZmxhdCAoaS5lLiwgc3RhYmxlKS4NCg0KLSBOb3Qgc2V0dGluZyBhbnkgYG1pbl9jb3VudGAgcmVzdWx0ZWQgaW4gYSBzdGVlcCwgdXB3YXJkLXNsb3BpbmcgdHJlbmQgbGluZS4NCi0gU2V0dGluZyBpdCB0byA1IGFsc28gcmVzdWx0ZWQgaW4gYSBzdGVlcCwgdXB3YXJkLXNsb3BpbmcgdHJlbmQgbGluZS4NCg0KYGBge3J9DQpkZHMgPC0gZmlsdGVyX2dlbmVzKGRkcywgbWluX2NvdW50ID0gMTApDQpgYGANCg0KVHJhbnNmb3JtIHRoZSByZWFkIGNvdW50cy4NCg0KRnJvbSBodHRwczovL2NoaXBzdGVyLmNzYy5maS9tYW51YWwvZGVzZXEyLXRyYW5zZm9ybS5odG1sOiA8YnI+DQpZb3UgY2FuIHVzZSB0aGUgcmVzdWx0aW5nIHRyYW5zZm9ybWVkIHZhbHVlcyBvbmx5IGZvciB2aXN1YWxpemF0aW9uIGFuZCBjbHVzdGVyaW5nLCBub3QgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHdoaWNoIG5lZWRzIHJhdyBjb3VudHMuDQoNCmBgYHtyfQ0KdnNkIDwtIHZzdChkZHMpDQptZWFuX3NkX3Bsb3QodnNkKQ0KYGBgDQoNCkNoZWNrIHRoZSBjbHVzdGVyaW5nIG9mIHRoZSBzYW1wbGVzLiANCg0KSWYgeW91IGVuY291bnRlciB0aGUgZXJyb3IgYEVycm9yIGluIGxvYWROYW1lc3BhY2UoeCkgOiB0aGVyZSBpcyBubyBwYWNrYWdlIGNhbGxlZCAnQ29tcGxleEhlYXRtYXAnYCwgdW5jb21tZW50IGFuZCBydW4gdGhlIGZvbGxvd2luZyBjb2RlIGJsb2NrOg0KDQpgYGB7cn0NCiMgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImpva2VyZ29vL0NvbXBsZXhIZWF0bWFwIikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9MTV9DQpzZXQuc2VlZCg0MikNCnBsb3Rfc2FtcGxlX2NsdXN0ZXJpbmcodnNkLCBhbm5vX3ZhcnMgPSBjKCJ0eXBlIiksIGRpc3RhbmNlID0gImV1Y2xpZGVhbiIpDQpgYGANCg0KUGVyZm9ybSBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIChQQ0EpLg0KDQpgYGB7cn0NCnBsb3RfcGNhKHZzZCwgUENfeCA9IDEsIFBDX3kgPSAyLCBzaGFwZV9ieSA9ICJ0eXBlIikNCmBgYA0KIyMjIFJlZ3VsYXRlZCBDZWxsIERlYXRoDQoNClJlZmVyIHRvIGAxLiBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIC0gTVNpZ0RCIEdlbmUgU2V0cyArIEdURXggVFBNLnJtZGAgZm9yIG1vcmUgZGV0YWlsZWQgZG9jdW1lbnRhdGlvbiBvbiBvYnRhaW5pbmcgdGhlIGdlbmUgc2V0cy4NCg0KIyMjIyBOZWNyb3B0b3Npcw0KDQpGZXRjaCB0aGUgbmVjcm9wdG9zaXMgZ2VuZSBzZXQuDQoNCmBgYHtyfQ0KbmVjcm9wdG9zaXMuZ2VuZXMgPC0gbXNpZ2RicihzcGVjaWVzID0gImh1bWFuIiwgY2F0ZWdvcnkgPSAiQzUiLCBzdWJjYXRlZ29yeSA9ICJHTzpCUCIpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKGdzX25hbWUgPT0gIkdPQlBfTkVDUk9QVE9USUNfU0lHTkFMSU5HX1BBVEhXQVkiKQ0KbmVjcm9wdG9zaXMuZ2VuZXMNCmBgYA0KRmlsdGVyIHRoZSBnZW5lcyB0byBpbmNsdWRlIG9ubHkgdGhvc2UgaW4gdGhlIG5lY3JvcHRvc2lzIGdlbmUgc2V0Lg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHJlc3VsdHM9ImhpZGUifQ0Kcm93bmFtZXMobmVjcm9wdG9zaXMuZ2VuZXMpIDwtIG5lY3JvcHRvc2lzLmdlbmVzJGVuc2VtYmxfZ2VuZQ0KY29hZF9uZWNyb3B0b3NpcyA8LSBjb3VudF9tYXRyaXhbcm93bmFtZXMoY291bnRfbWF0cml4KSAlaW4lIG5lY3JvcHRvc2lzLmdlbmVzJGVuc2VtYmxfZ2VuZSwgXQ0KY29hZF9uZWNyb3B0b3NpcyA8LSBjb2FkX25lY3JvcHRvc2lzWywgcm93bmFtZXMoc2FtcGxlcyldDQoNCiMgQ2hlY2sgaWYgYWxsIHNhbXBsZXMgaW4gdGhlIGNvdW50cyBkYXRhZnJhbWUgYXJlIGluIHRoZSBzYW1wbGVzIGRhdGFmcmFtZQ0KYWxsKGNvbG5hbWVzKGNvYWRfbmVjcm9wdG9zaXMpID09IHJvd25hbWVzKHNhbXBsZXMpKQ0KYGBgDQoNClBlcmZvcm0gZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcy4NCg0KYGBge3J9DQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeCgNCiAgY291bnREYXRhID0gY29hZF9uZWNyb3B0b3NpcywNCiAgY29sRGF0YSA9IHNhbXBsZXMsDQogIGRlc2lnbiA9IH50eXBlDQopDQpkZHMgPC0gZmlsdGVyX2dlbmVzKGRkcywgbWluX2NvdW50ID0gMTApDQpkZHMkdHlwZSA8LSByZWxldmVsKGRkcyR0eXBlLCByZWYgPSAibm9ybWFsIikNCmRkcyA8LSBERVNlcShkZHMpDQpyZXMgPC0gcmVzdWx0cyhkZHMpDQpzdW1tYXJ5KHJlcykNCmBgYA0KDQpQcmV0dGlmeSB0aGUgZGlzcGxheSBvZiByZXN1bHRzLg0KDQpgYGB7cn0NCmRlc2VxLnJlc3VsdHMgPC0gcmVzDQpkZXNlcS5iYmwuZGF0YSA8LSBkYXRhLmZyYW1lKA0KICByb3cubmFtZXMgPSByb3duYW1lcyhkZXNlcS5yZXN1bHRzKSwNCiAgYmFzZU1lYW4gPSBkZXNlcS5yZXN1bHRzJGJhc2VNZWFuLA0KICBsb2cyRm9sZENoYW5nZSA9IGRlc2VxLnJlc3VsdHMkbG9nMkZvbGRDaGFuZ2UsDQogIGxmY1NFID0gZGVzZXEucmVzdWx0cyRsZmNTRSwNCiAgc3RhdCA9IGRlc2VxLnJlc3VsdHMkc3RhdCwNCiAgcHZhbHVlID0gZGVzZXEucmVzdWx0cyRwdmFsdWUsDQogIHBhZGogPSBkZXNlcS5yZXN1bHRzJHBhZGosDQogIGNhbmNlcl90eXBlID0gIkNvbG9uIiwNCiAgZ2VuZV9zeW1ib2wgPSBuZWNyb3B0b3Npcy5nZW5lc1tyb3duYW1lcyhkZXNlcS5yZXN1bHRzKSwgImdlbmVfc3ltYm9sIl0NCikNCmRlc2VxLmJibC5kYXRhDQpgYGANCg0KUGxvdCB0aGUgcmVzdWx0cy4NCg0KYGBge3J9DQpnZ3Bsb3QoZGVzZXEuYmJsLmRhdGEsIGFlcyh4ID0gY2FuY2VyX3R5cGUsIHkgPSBnZW5lX3N5bWJvbCwgc2l6ZSA9IHBhZGosIGZpbGwgPSBsb2cyRm9sZENoYW5nZSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2hhcGUgPSAyMSwgY29sb3IgPSAiYmxhY2siKSArDQogIHNjYWxlX3NpemUodHJhbnMgPSAicmV2ZXJzZSIpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gImJsdWUiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gInJlZCIsIGxpbWl0cyA9IGMobWluKGRlc2VxLmJibC5kYXRhJGxvZzJGb2xkQ2hhbmdlKSwgbWF4KGRlc2VxLmJibC5kYXRhJGxvZzJGb2xkQ2hhbmdlKSkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgbGFicyhzaXplID0gIkZEUiIsIGZpbGwgPSAibG9nMiBGQyIsIHggPSAiQ2FuY2VyIHR5cGUiLCB5ID0gIkdlbmUiKQ0KYGBgDQojIyMjIEZlcnJvcHRvc2lzDQoNCkZldGNoIHRoZSBmZXJyb3B0b3NpcyBnZW5lIHNldC4NCg0KYGBge3J9DQpmZXJyb3B0b3Npcy5nZW5lcyA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiaHVtYW4iLCBjYXRlZ29yeSA9ICJDMiIsIHN1YmNhdGVnb3J5ID0gIkNQOldJS0lQQVRIV0FZUyIpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKGdzX25hbWUgPT0gIldQX0ZFUlJPUFRPU0lTIikNCmZlcnJvcHRvc2lzLmdlbmVzDQpgYGANCg0KRmlsdGVyIHRoZSBnZW5lcyB0byBpbmNsdWRlIG9ubHkgdGhvc2UgaW4gdGhlIGZlcnJvcHRvc2lzIGdlbmUgc2V0Lg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHJlc3VsdHM9ImhpZGUifQ0Kcm93bmFtZXMoZmVycm9wdG9zaXMuZ2VuZXMpIDwtIGZlcnJvcHRvc2lzLmdlbmVzJGVuc2VtYmxfZ2VuZQ0KY29hZF9mZXJyb3B0b3NpcyA8LSBjb3VudF9tYXRyaXhbcm93bmFtZXMoY291bnRfbWF0cml4KSAlaW4lIGZlcnJvcHRvc2lzLmdlbmVzJGVuc2VtYmxfZ2VuZSwgXQ0KY29hZF9mZXJyb3B0b3NpcyA8LSBjb2FkX2ZlcnJvcHRvc2lzWywgcm93bmFtZXMoc2FtcGxlcyldDQoNCiMgQ2hlY2sgaWYgYWxsIHNhbXBsZXMgaW4gdGhlIGNvdW50cyBkYXRhZnJhbWUgYXJlIGluIHRoZSBzYW1wbGVzIGRhdGFmcmFtZQ0KYWxsKGNvbG5hbWVzKGNvYWRfZmVycm9wdG9zaXMpID09IHJvd25hbWVzKHNhbXBsZXMpKQ0KYGBgDQpQZXJmb3JtIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMuDQoNCmBgYHtyfQ0KZGRzIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoDQogIGNvdW50RGF0YSA9IGNvYWRfZmVycm9wdG9zaXMsDQogIGNvbERhdGEgPSBzYW1wbGVzLA0KICBkZXNpZ24gPSB+dHlwZQ0KKQ0KZGRzIDwtIGZpbHRlcl9nZW5lcyhkZHMsIG1pbl9jb3VudCA9IDEwKQ0KZGRzJHR5cGUgPC0gcmVsZXZlbChkZHMkdHlwZSwgcmVmID0gIm5vcm1hbCIpDQpkZHMgPC0gREVTZXEoZGRzKQ0KcmVzIDwtIHJlc3VsdHMoZGRzKQ0Kc3VtbWFyeShyZXMpDQpgYGANClByZXR0aWZ5IHRoZSBkaXNwbGF5IG9mIHJlc3VsdHMuDQoNCmBgYHtyfQ0KZGVzZXEucmVzdWx0cyA8LSByZXMNCmRlc2VxLmJibC5kYXRhIDwtIGRhdGEuZnJhbWUoDQogIHJvdy5uYW1lcyA9IHJvd25hbWVzKGRlc2VxLnJlc3VsdHMpLA0KICBiYXNlTWVhbiA9IGRlc2VxLnJlc3VsdHMkYmFzZU1lYW4sDQogIGxvZzJGb2xkQ2hhbmdlID0gZGVzZXEucmVzdWx0cyRsb2cyRm9sZENoYW5nZSwNCiAgbGZjU0UgPSBkZXNlcS5yZXN1bHRzJGxmY1NFLA0KICBzdGF0ID0gZGVzZXEucmVzdWx0cyRzdGF0LA0KICBwdmFsdWUgPSBkZXNlcS5yZXN1bHRzJHB2YWx1ZSwNCiAgcGFkaiA9IGRlc2VxLnJlc3VsdHMkcGFkaiwNCiAgY2FuY2VyX3R5cGUgPSAiQ29sb24iLA0KICBnZW5lX3N5bWJvbCA9IGZlcnJvcHRvc2lzLmdlbmVzW3Jvd25hbWVzKGRlc2VxLnJlc3VsdHMpLCAiZ2VuZV9zeW1ib2wiXQ0KKQ0KZGVzZXEuYmJsLmRhdGENCmBgYA0KUGxvdCB0aGUgcmVzdWx0cy4NCg0KYGBge3IsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTE1fQ0KZ2dwbG90KGRlc2VxLmJibC5kYXRhLCBhZXMoeCA9IGNhbmNlcl90eXBlLCB5ID0gZ2VuZV9zeW1ib2wsIHNpemUgPSBwYWRqLCBmaWxsID0gbG9nMkZvbGRDaGFuZ2UpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNoYXBlID0gMjEsIGNvbG9yID0gImJsYWNrIikgKw0KICBzY2FsZV9zaXplKHRyYW5zID0gInJldmVyc2UiKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJibHVlIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICJyZWQiLCBsaW1pdHMgPSBjKG1pbihkZXNlcS5iYmwuZGF0YSRsb2cyRm9sZENoYW5nZSksIG1heChkZXNlcS5iYmwuZGF0YSRsb2cyRm9sZENoYW5nZSkpKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIGxhYnMoc2l6ZSA9ICJGRFIiLCBmaWxsID0gImxvZzIgRkMiLCB4ID0gIkNhbmNlciB0eXBlIiwgeSA9ICJHZW5lIikNCmBgYA0KIyMjIyBQeXJvcHRvc2lzDQoNCkZldGNoIHRoZSBweXJvcHRvc2lzIGdlbmUgc2V0Lg0KDQpgYGB7cn0NCnB5cm9wdG9zaXMuZ2VuZXMgPC0gbXNpZ2RicihzcGVjaWVzID0gImh1bWFuIiwgY2F0ZWdvcnkgPSAiQzIiLCBzdWJjYXRlZ29yeSA9ICJDUDpSRUFDVE9NRSIpICU+JQ0KICBkcGx5cjo6ZmlsdGVyKGdzX25hbWUgPT0gIlJFQUNUT01FX1BZUk9QVE9TSVMiKQ0KcHlyb3B0b3Npcy5nZW5lcw0KYGBgDQoNCkZpbHRlciB0aGUgZ2VuZXMgdG8gaW5jbHVkZSBvbmx5IHRob3NlIGluIHRoZSBweXJvcHRvc2lzIGdlbmUgc2V0Lg0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHJlc3VsdHM9ImhpZGUifQ0Kcm93bmFtZXMocHlyb3B0b3Npcy5nZW5lcykgPC0gcHlyb3B0b3Npcy5nZW5lcyRlbnNlbWJsX2dlbmUNCmNvYWRfcHlyb3B0b3NpcyA8LSBjb3VudF9tYXRyaXhbcm93bmFtZXMoY291bnRfbWF0cml4KSAlaW4lIHB5cm9wdG9zaXMuZ2VuZXMkZW5zZW1ibF9nZW5lLCBdDQpjb2FkX3B5cm9wdG9zaXMgPC0gY29hZF9weXJvcHRvc2lzWywgcm93bmFtZXMoc2FtcGxlcyldDQoNCiMgQ2hlY2sgaWYgYWxsIHNhbXBsZXMgaW4gdGhlIGNvdW50cyBkYXRhZnJhbWUgYXJlIGluIHRoZSBzYW1wbGVzIGRhdGFmcmFtZQ0KYWxsKGNvbG5hbWVzKGNvYWRfcHlyb3B0b3NpcykgPT0gcm93bmFtZXMoc2FtcGxlcykpDQpgYGANCg0KUGVyZm9ybSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFuYWx5c2lzLg0KDQpgYGB7cn0NCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KA0KICBjb3VudERhdGEgPSBjb2FkX3B5cm9wdG9zaXMsDQogIGNvbERhdGEgPSBzYW1wbGVzLA0KICBkZXNpZ24gPSB+dHlwZQ0KKQ0KZGRzIDwtIGZpbHRlcl9nZW5lcyhkZHMsIG1pbl9jb3VudCA9IDEwKQ0KZGRzJHR5cGUgPC0gcmVsZXZlbChkZHMkdHlwZSwgcmVmID0gIm5vcm1hbCIpDQpkZHMgPC0gREVTZXEoZGRzKQ0KcmVzIDwtIHJlc3VsdHMoZGRzKQ0Kc3VtbWFyeShyZXMpDQpgYGANClByZXR0aWZ5IHRoZSBkaXNwbGF5IG9mIHJlc3VsdHMuDQoNCmBgYHtyfQ0KZGVzZXEucmVzdWx0cyA8LSByZXMNCmRlc2VxLmJibC5kYXRhIDwtIGRhdGEuZnJhbWUoDQogIHJvdy5uYW1lcyA9IHJvd25hbWVzKGRlc2VxLnJlc3VsdHMpLA0KICBiYXNlTWVhbiA9IGRlc2VxLnJlc3VsdHMkYmFzZU1lYW4sDQogIGxvZzJGb2xkQ2hhbmdlID0gZGVzZXEucmVzdWx0cyRsb2cyRm9sZENoYW5nZSwNCiAgbGZjU0UgPSBkZXNlcS5yZXN1bHRzJGxmY1NFLA0KICBzdGF0ID0gZGVzZXEucmVzdWx0cyRzdGF0LA0KICBwdmFsdWUgPSBkZXNlcS5yZXN1bHRzJHB2YWx1ZSwNCiAgcGFkaiA9IGRlc2VxLnJlc3VsdHMkcGFkaiwNCiAgY2FuY2VyX3R5cGUgPSAiQ29sb24iLA0KICBnZW5lX3N5bWJvbCA9IHB5cm9wdG9zaXMuZ2VuZXNbcm93bmFtZXMoZGVzZXEucmVzdWx0cyksICJnZW5lX3N5bWJvbCJdDQopDQpkZXNlcS5iYmwuZGF0YQ0KYGBgDQpQbG90IHRoZSByZXN1bHRzLg0KDQpgYGB7ciwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9MTB9DQpnZ3Bsb3QoZGVzZXEuYmJsLmRhdGEsIGFlcyh4ID0gY2FuY2VyX3R5cGUsIHkgPSBnZW5lX3N5bWJvbCwgc2l6ZSA9IHBhZGosIGZpbGwgPSBsb2cyRm9sZENoYW5nZSkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2hhcGUgPSAyMSwgY29sb3IgPSAiYmxhY2siKSArDQogIHNjYWxlX3NpemUodHJhbnMgPSAicmV2ZXJzZSIpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobG93ID0gImJsdWUiLCBtaWQgPSAid2hpdGUiLCBoaWdoID0gInJlZCIsIGxpbWl0cyA9IGMobWluKGRlc2VxLmJibC5kYXRhJGxvZzJGb2xkQ2hhbmdlKSwgbWF4KGRlc2VxLmJibC5kYXRhJGxvZzJGb2xkQ2hhbmdlKSkpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgbGFicyhzaXplID0gIkZEUiIsIGZpbGwgPSAibG9nMiBGQyIsIHggPSAiQ2FuY2VyIHR5cGUiLCB5ID0gIkdlbmUiKQ0KYGBg